use std::{
    cell::RefCell,
    collections::HashMap,
    io::{Read, Write},
    rc::Rc,
    thread,
    time::Duration,
};

use http_muncher::Parser;
use mio::net::TcpStream;

use crate::server::utils::gen_key;

use super::{
    frame::{Frame, OpCode},
    handler::HttpHandler,
};

#[derive(PartialEq, Debug)]
pub enum ClientState {
    AwaitingHandshake,
    HandshakeResponse,
    Connected,
    Closed,
}

#[derive(Debug)]
pub struct Client {
    pub socket: TcpStream,
    // Add outgoing frames queue:
    outgoing: Vec<Frame>,
    pub headers: Rc<RefCell<HashMap<String, String>>>,
    pub state: ClientState,
}
impl Client {
    // Create a new WebSocketClient
    pub fn new(socket: TcpStream) -> Self {
        let headers = Rc::new(RefCell::new(HashMap::new()));
        Self {
            socket,
            headers,
            state: ClientState::AwaitingHandshake,
            outgoing: Vec::new(),
        }
    }

    pub fn get_addr(&self) -> String {
        self.socket.peer_addr().unwrap().to_string()
    }

    fn read_handshake(&mut self) {
        let mut handler = HttpHandler::new(self.headers.clone());
        let mut parser = Parser::request();

        let is_upgrade = loop {
            let mut buf = [0u8; 1024];
            match self.socket.read(&mut buf) {
                Err(e) => {
                    log::warn!("failed to read from socket: {}", e);
                    break false;
                }
                Ok(l) => {
                    if l == 0 {
                        break false;
                    }
                    parser.parse(&mut handler, &buf[0..l]);
                    log::debug!("parser is upgrade: {}", parser.is_upgrade());
                    break parser.is_upgrade();
                }
            };
        };
        log::debug!("upgrade: {}", is_upgrade);
        if is_upgrade {
            self.state = ClientState::HandshakeResponse;
        }
    }

    fn write_handshake(&mut self) -> Result<(), String> {
        let headers = self.headers.borrow();
        let key = headers
            .get("Sec-WebSocket-Key")
            .ok_or("invalid key".to_string())?;
        log::debug!("key: {}", key);
        let response_key = gen_key(key);
        let response = format!(
            "HTTP/1.1 101 Switching Protocols\r\n\
            Connection: Upgrade\r\n\
            Sec-WebSocket-Accept: {}\r\n\
            Upgrade: websocket\r\n\r\n",
            response_key
        );

        match self.socket.write_all(response.as_bytes()) {
            Ok(_) => {
                self.state = ClientState::Connected;
            }
            Err(e) => return Err(e.to_string()),
        }
        Ok(())
    }

    fn read_frame(&mut self) {
        match Frame::from_read(&mut self.socket) {
            Ok(f) => match f.header.opcode {
                OpCode::TextFrame => {
                    self.outgoing.push(Frame::from("Hi there!"));
                }
                OpCode::ConnectionClose => {
                    self.outgoing.push(Frame::close(&f));
                }
                OpCode::Ping => {
                    self.outgoing.push(Frame::pong(&f));
                }
                _ => {}
            },
            Err(e) => {
                log::warn!("failed to read frame: {}", e);
            }
        }
    }
    pub fn read(&mut self) {
        thread::sleep(Duration::from_nanos(50));
        match self.state {
            ClientState::AwaitingHandshake => self.read_handshake(),
            ClientState::Connected => self.read_frame(),
            _ => (),
        }
    }

    pub fn write(&mut self) {
        match self.state {
            ClientState::AwaitingHandshake => {
                self.state = ClientState::Closed;
                self.socket.shutdown(std::net::Shutdown::Both).unwrap();
            }
            ClientState::HandshakeResponse => {
                if let Err(e) = self.write_handshake() {
                    log::warn!("failed to write handshake response: {}", e)
                }
            }
            ClientState::Connected => self.handle_message(),
            _ => {}
        }
    }

    pub fn handle_message(&mut self) {
        log::info!("sending {} frames", self.outgoing.len());
        for frame in &self.outgoing {
            let buf = frame.serialize();
            self.socket.write(&buf).unwrap();

            if frame.is_close() {
                self.state = ClientState::Closed;
                self.socket.shutdown(std::net::Shutdown::Both).unwrap();
            }
        }

        self.outgoing.clear();
    }
}
